﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace BDDfyIt.CodeDom
{
    public class CodeDomWrapper
    {
        private CodeNamespace NameSpace { get; set; }

        private CodeClass Class { get; set; }

        private CodeMemberMethod Method { get; set; }

        private CodeCompileUnit CompileUnit { get; set; }

        public void CreateNamespace(string name)
        {
            NameSpace = new CodeNamespace { Name = name };
        }

        public void CreateImports(List<string> importNames)
        {
            foreach (var import in importNames)
            {
                NameSpace.Imports.Add(new CodeNamespaceImport(import));
            }
        }

        public void CreateClass(string name, string accessModifier)
        {
            Class = new CodeClass { Name = name, AccessModifier = accessModifier };
            //NameSpace.Types.Add(Class);
        }

        public void CreateCustomAttributeOnClass(string attributeName)
        {
            var customAttribute = new CodeAttributeDeclaration(attributeName);
            Class.CustomAttributes.Add(customAttribute);
        }

        public void CreateCustomAttributeOnClass(Type attributeType, List<string> attributeArgument = null, List<string> attributeArgumentValue = null)
        {
            var customAttribute = new CodeAttributeDeclaration(new CodeTypeReference(attributeType));
            if ((attributeArgument != null && attributeArgument.Count > 0) && (attributeArgumentValue != null && attributeArgumentValue.Count > 0))
            {
                for (var i = 0; i < attributeArgument.Count; i++)
                {
                    var codeAttr = new CodeAttributeArgument(attributeArgument[i],
                                                             new CodePrimitiveExpression(attributeArgumentValue[i]));
                    customAttribute.Arguments.Add(codeAttr);
                }
            }
            Class.CustomAttributes.Add(customAttribute);
        }

        public void CreateCustomAttributeOnMethod(string attributeName)
        {
            var customAttribute = new CodeAttributeDeclaration(attributeName);
            Method.CustomAttributes.Add(customAttribute);
        }

        public void CreateCustomAttributeOnMethod(Type attributeType, List<string> attributeArgument = null, List<string> attributeArgumentValue = null)
        {
            var customAttribute = new CodeAttributeDeclaration(new CodeTypeReference(attributeType));
            if ((attributeArgument != null && attributeArgument.Count > 0) && (attributeArgumentValue != null && attributeArgumentValue.Count > 0))
            {
                for (var i = 0; i < attributeArgument.Count; i++)
                {
                    var codeAttr = new CodeAttributeArgument(attributeArgument[i],
                                                             new CodePrimitiveExpression(attributeArgumentValue[i]));
                    customAttribute.Arguments.Add(codeAttr);
                }
            }
            Method.CustomAttributes.Add(customAttribute);
        }

        public void CreateMethod(string methodName, string accessModifier, List<Type> parameterTypes, List<string> parameterNames, Type returnType)
        {
            Method = new CodeMemberMethod();
            Method.Name = methodName;
            Method.Attributes = Extensions.ParseEnum(accessModifier, MemberAttributes.Public) | MemberAttributes.Final;
            for (var i = 0; i < parameterTypes.Count; i++)
            {
                var parameter = new CodeParameterDeclarationExpression(new CodeTypeReference(parameterTypes[i]),
                                                                       parameterNames[i]);
                Method.Parameters.Add(parameter);
            }
            Method.ReturnType = new CodeTypeReference(returnType);
        }

        public void CreateVoidMethod(string methodName, string accessModifier)
        {
            CreateMethod(methodName, accessModifier, new List<Type>(), new List<string>(), typeof(void));
        }

        public void AddExecuteCodeToMethod(string className)
        {
            string runStatement = string.Format("(new {0}()).BDDfy()", className);
            var expression = new CodeSnippetExpression(runStatement);
            Method.Statements.Add(expression);
        }

        public void AddCommentToMethod(string comment)
        {
            var codeComment = new CodeCommentStatement(comment);
            Method.Comments.Add(codeComment);
        }

        public void AddCommentToClass(string comment)
        {
            var codeComment = new CodeCommentStatement(comment);
            Class.Comments.Add(codeComment);
        }

        public void AddClassToNameSpace()
        {
            NameSpace.Types.Add(Class);
        }

        public void AddMethodToClass()
        {
            Class.Members.Add(Method);
        }

        public void GenerateCSharpCode(string fileName)
        {
            CompileUnit = new CodeCompileUnit();
            CompileUnit.Namespaces.Add(NameSpace);
            var provider = CodeDomProvider.CreateProvider("CSharp");
            var options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            using (var sourceWriter = new StreamWriter(fileName))
            {
                provider.GenerateCodeFromCompileUnit(CompileUnit, sourceWriter, options);
            }
        }
    }
}
